﻿using System;
using System.Collections.Generic;

namespace LOHDemo
{
	// This test will allocate two lists of objects
	// interlaced with each other onto the large object heap.
	// This results in intertwined memory, we can then release
	// one of the lists, force it to be garbage collected and we'll 
	// find that because it's on the large object heap, it doesn't get
	// compacted. As such, the largest object we can then allocate is
	// the size of the largest hole, which in this case will be 1Mb.
	// Even if we have 100's of Mb of free space on the heap, the 
	// fragmentation will prevent us from allocating anything larger 
	// than 1Mb, or it's largest hole.
	public class TestInterlacedMemoryAllocationResultsInOutOfMemory : ITest
	{
		public void Execute(IHost host)
		{
			host.StartTest("Test one - filling LOH with interlaced small objects");
			host.MemoryUsed("Start of test");
			List<OneMegObject> first = new List<OneMegObject>();
			List<OneMegObject> second = new List<OneMegObject>();

			// Allocate interlaced memory until it runs out
			try
			{
				while (true)
				{
					first.Add(new OneMegObject());
					second.Add(new OneMegObject());
				}
			}
			catch (OutOfMemoryException)
			{
			}

			// LOH is now fill
			host.MemoryUsed("Memory is now full.");

			// Clear one of the lists which will result in the heap having
			// half memory free, but in 1mb holes. Notice we force a full 
			// generation 2 collection.
			first.Clear();
			first = null;
			GC.Collect(2, GCCollectionMode.Forced);
			host.MemoryUsed("After freeing first list.");

			// Now we have plenty of free space on the heap, let's try to allocate
			// a small 3mb chunk of memory.
			try
			{
				byte[] wontfit = new byte[Constants.ThreeMeg];
				host.Error("!!!!!!!!!!!!!!!!!! SHOULD NOT HAVE REACHED THIS !!!!!!!!!!!!!!");
			}
			catch (OutOfMemoryException e)
			{
				host.Error("As predicted we received an out of memory exception - not enough continuous space on the heap");
			}

			// Clean up after ourselves, release the second list and force collection
			second.Clear();
			second = null;
			GC.Collect(2, GCCollectionMode.Forced);

			host.MemoryUsed("All memory cleaned up");
		}
	}
}
